home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d11 / underli2.arc / UNDERLI2.ASM < prev    next >
Encoding:
Assembly Source File  |  1988-01-22  |  42.5 KB  |  1,115 lines

  1.           .XLIST
  2.           PAGE   60,132
  3.           TITLE  UNDERLI2.ASM - Alters BIOS registers to enable underlining
  4.           SUBTTL Opening Comments
  5.           .LIST
  6. ;-----------------------------------------------------------------------------
  7. ; SUMMARY
  8. ;-----------------------------------------------------------------------------
  9.  
  10. ;              Turn EGA underlining on or off.  Also change specified
  11. ;         palette values for a mode command.
  12. ;
  13. ;         Usage
  14. ;                underli2 [on|off|switch|keep|unload|#] [ /s] [/p<k1>=<n1>] 
  15. ;                         [/p<k2>=<n2>] ...
  16. ;                where
  17. ;                         "on" turns underlining on.
  18. ;                         "off" turns underline off.
  19. ;                         "switch" reverses the underlining setting.
  20. ;                         "keep" keeps the underlining setting.
  21. ;                         "unload" removes underli2 from memory.
  22. ;                         # turns on underlining at scan line #.
  23. ;                              If you don't specify one of these values, 
  24. ;                         underli2 reverses the underlining setting.
  25. ;                              "/s" resets all unspecified palettes to their
  26. ;                         standard values.
  27. ;                              "/p<ki>=<ni>" sets palette ki (maximum 15)
  28. ;                         to value ni (maximum 63 = 03FH).  Enter ki and ni
  29. ;                         as decimal numbers.
  30. ;         Upper and lower case are both OK.  You can use "-" in place of "/".
  31. ;         The = separator between <ki> and <ni> can be any non-numeric
  32. ;         character.  You can use any sequence of command line arguments.
  33. ;
  34. ;         ERROR LEVEL values returned
  35. ;              0     UNDERLI2 succeeded.
  36. ;              1     No EGA present.
  37. ;              2     Usage error.
  38. ;              3     Not DOS 2.x or DOS 3.x.
  39. ;              4     IS_RESIDENT found a memory allocation error.
  40. ;              5           UNDERLI2 tried to unload a resident incarnation,
  41. ;                    but IS_RESIDENT couldn't find one.
  42. ;              6           UNDERLI2 tried to unload a resident incarnation,
  43. ;                    but there was another program chained to INT 10H after
  44. ;                    the incarnation IS_RESIDENT found.
  45. ;              7           DOS function 49H (Release memory) failed.
  46. ;
  47. ;              This program modifies the Enhanced Color Adapter's BIOS regs
  48. ;         to allow underlining to be displayed with the enhanced graphics
  49. ;         adapter and to change selected palette values. Because many `
  50. ;         programs initialize the display prior to using it, any fixes to
  51. ;         the BIOS registers will be reset back to their initial states.
  52. ;         This program will correct the BIOS register problems after each
  53. ;         reset.
  54. ;
  55. ;         REQUIREMENTS:                Microsoft Macro Assembler 4.0
  56. ;                                      IBM Linker
  57. ;                                      IBM EXE2BIN
  58. ;                                      ISRESDNT.OBJ
  59. ;
  60. ;         COMPILATION:                 MASM UNDERLI2;
  61. ;                                      LINK UNDERLI2 ISRESDNT;
  62. ;                                      EXE2BIN UNDERLI2 UNDERLI2.COM
  63. ;
  64. ;
  65. ;              ISRESDNT is a subroutine which I wrote to determine
  66. ;         whether a program is already resident.  After it is run:
  67. ;              AX indicates the result of the procedure:
  68. ;                  =  0 if it does not find a copy already resident
  69. ;                  =  1 if it finds a copy already resident
  70. ;                  = -1 if the DOS major version is not legal
  71. ;                  = -2 if there is a memory allocation error
  72. ;              CF is set if a copy of this program is resident or if the
  73. ;         DOS version is not legal
  74. ;              DS = DS for the first copy of this program in memory.  Note
  75. ;                   that if no earlier copy is resident, it is DS of the
  76. ;                   calling program.
  77. ;              ES = DS for the calling program.
  78. ;
  79. ;              I based UNDERLI2.ASM on Gerald S. Kaplan's UNDERLIN.ASM,
  80. ;         which he published in "PC Tech Journal", Vol. 5, No. 9
  81. ;         (September, 1987), p. 49.
  82.  
  83.  
  84. ;         Author      : Lew Paper
  85. ;         Date written: 12/15/87
  86. ;         Revisions   : 
  87.  
  88.           .XLIST
  89.           SUBTTL Switches, Directives, Equates, Macros and Start
  90.           .LIST
  91.           PAGE
  92. ;-----------------------------------------------------------------------------
  93. ; SWITCH SETTINGS
  94. ;-----------------------------------------------------------------------------
  95.  
  96. resident_int EQU  10H
  97.  
  98. prototype EQU    0
  99.  
  100. ;-----------------------------------------------------------------------------
  101. ; INITIAL DIRECTIVES
  102. ;-----------------------------------------------------------------------------
  103.  
  104.           EXTRN is_resident:NEAR
  105.  
  106. ;Standard handles
  107. StdIn     EQU    0                     ; Standard input device
  108. StdOut    EQU    1                     ; Standard output device
  109. StdErr    EQU    2                     ; Standard error device
  110. StdAux    EQU    3                     ; Standard auxiliary device
  111. StdPrn    EQU    4                     ; Standard printer device
  112.  
  113. FALSE     EQU      0                   ; FALSE compare
  114. TRUE      EQU    0FFFFH                ; TRUE compare
  115.  
  116. tab       EQU    09h                   ; ASCII tab
  117. lf        EQU    0Ah                   ; line feed
  118. cr        EQU    0Dh                   ; carriage return
  119. blank     EQU    20h                   ; ASCII blank
  120.  
  121. cmdtail   EQU    80h                   ; Offset of command tail
  122.  
  123. doscall   MACRO
  124.           INT    21h                   ; call MS-DOS function
  125.           ENDM                         ; doscall
  126.  
  127. ;; DISplay STRing from memory
  128. dis_str   MACRO  string                ;; display a string
  129.           MOV    DX,OFFSET string
  130.           MOV    AH,09h
  131.           doscall
  132.           ENDM                         ; dis_str
  133.  
  134. ;;      Macro to write string to handle.  Note that it can change AX,
  135. ;; BX, CX, DX.  It assumes that DS contains the data segment for string.
  136. ;; It depends on string_length containing the number of bytes in the string. 
  137. write_str MACRO  string, handle
  138.           MOV    AH,40H
  139.           MOV    BX,handle
  140.           MOV    CX,string&_length
  141.           LEA    DX,string
  142.           doscall
  143.           ENDM                         ; write_str
  144.  
  145. ;; String to compare a command line argument to and its length
  146. underline_action_string MACRO name
  147. name&_value DB "&name"
  148. name&_length EQU $ - OFFSET name&_value
  149.           ENDM                         ; underline_action_string
  150.  
  151. ;;      Check whether the command line argument is "name".  If so, set
  152. ;; underline_action and go on to next argument.  Otherwise, fall through
  153. ;; to end.
  154. ;;      check_underline_action depends on the convention that the string
  155. ;; of "name" is in name&_string and its length is in name&_length.
  156. ;; MACRO underline_action_string constructs these variables.
  157. check_underline_action MACRO name
  158.           LOCAL  not_name
  159.           CMP    CX,name&_length       ;; Is it worth comparing?
  160.           JNZ    not_name              ;; No
  161.           PUSH   CX                    ;; Length of argument
  162.           PUSH   SI                    ;; Offset of argument
  163.           MOV    DI,OFFSET name&_value ;; String to check
  164.           REPZ   CMPSB                 ;; Compare them
  165.           POP    SI                    ;; Restore offset of argument
  166.           POP    CX                    ;; Restore length of argument
  167.           JNZ    not_name              ;; Not this parameter
  168.           MOV    underline_action,name
  169.           JMP    SHORT get_parameter_loop ;; Get next parameter
  170. not_name:
  171.           ENDM                         ; check_underline_action
  172.  
  173. ;;      Jump to not_a_digit if [BX] is not a digit.  Internal to MACRO
  174. ;; convert_bx_to_integer.
  175. test_for_digit MACRO not_a_digit
  176.           CMP    BYTE PTR [BX],'9'
  177.           JA     not_a_digit
  178.           CMP    BYTE PTR [BX],'0'
  179.           JB     not_a_digit
  180.           ENDM                         ; test_for_digit
  181.  
  182. ;;      Convert [BX] to an integer in AX.  At end, BX points to the next
  183. ;; character of the argument and CX is the count of characters remaining.
  184. ;; If the first character of [BX] is not an integer, set carry.
  185. convert_bx_to_integer MACRO 
  186.           LOCAL  not_an_integer, exit, convert_loop
  187.  
  188.           CLC                          ;; Clear carry for no error
  189.           test_for_digit not_an_integer ;; -1 => AX if first character
  190.                                        ;; is not a digit
  191.           XOR    AX,AX                 ;; 0 => AX
  192.           MOV    AL,[BX]
  193.           SUB    AX,'0'                ;; Convert to digit
  194. convert_loop:
  195.           INC    BX                    ;; Point at next digit
  196.           DEC    CX
  197.           JCXZ   exit                  ;; Argument done
  198.           test_for_digit exit          ;; Conversion done inside argument
  199.           MOV    DX,AX                 ;; Multiply number by 10 => DX
  200.           SHL    DX,1
  201.           SHL    DX,1
  202.           ADD    DX,AX
  203.           SHL    DX,1
  204.           XOR    AX,AX                 ;; Next digit to AX
  205.           MOV    AL,[BX]
  206.           SUB    AX,'0'
  207.           ADD    AX,DX
  208.           JMP    convert_loop
  209.  
  210. not_an_integer:
  211.           STC
  212.  
  213. exit:
  214.           ENDM                         ; convert_bx_to_integer
  215.  
  216. ;;      Change all palette values.  Assumes DS is data segment of resident
  217. ;; incarnation.
  218. change_palette MACRO
  219.           LOCAL  change_single_palettes, change_palette_loop, change_palette_exit
  220.  
  221.           CMP    reset_palette,0
  222.           JZ     change_single_palettes
  223.           PUSH   ES                    ;; Change all palettes and overscan
  224.           MOV    AX,DS
  225.           MOV    ES,AX
  226.           MOV    AX,1002H              ;; BIOS set palette call
  227.           MOV    DX,OFFSET all_palettes
  228.           INT    10H
  229.           POP    ES                    ;; Restore register
  230.           JMP    change_palette_exit
  231.  
  232. change_single_palettes:
  233.           MOV    CX,palette_value_count
  234.           JCXZ   change_palette_exit
  235.           MOV    SI,OFFSET palette_value
  236. change_palette_loop:
  237.           MOV    AX,1000H              ;; BIOS set palette call
  238.           MOV    BX,[SI]
  239.           INT    10H
  240.           INC    SI
  241.           INC    SI
  242.           LOOP   change_palette_loop
  243.  
  244. change_palette_exit:
  245.           ENDM                         ; change_palette
  246.  
  247. ;;      Set underlining register.  Assumes DS is data segment of resident
  248. ;; incarnation.
  249. set_underlining MACRO
  250.           PUSH   DS
  251.           MOV    AX,40H                ;; Set up addressibility of BIOS data
  252.           MOV    DS,AX
  253.           MOV    DX,DS:63H             ;; CRTC base address
  254.           MOV    AL,014H               ;; Register 14 is underline register
  255.           OUT    DX,AL                 ;; Indicate change to register 14
  256.           INC    DX                    ;; Now point to CRTC data register
  257.           POP    DS                    ;; Segment of resident incarnation
  258.           MOV    AL,scanline
  259.           OUT    DX,AL                 ;; Send it to register 14
  260.           ENDM                         ;; set_underlining
  261.  
  262. ; Values for scanline
  263. DEFAULT_ON_LINE_NUMBER  EQU 13
  264. OFF_LINE_NUMBER EQU 14
  265.  
  266. ; Values for underline_action
  267. NONE      EQU    0
  268. ON        EQU    1
  269. OFF       EQU    2
  270. SWITCH    EQU    3
  271. KEEP      EQU    4
  272. UNLOAD    EQU    5
  273. NUMBER    EQU    6
  274.  
  275. COMSEG    SEGMENT PARA PUBLIC 'CODE'
  276.           ASSUME CS:COMSEG,DS:COMSEG,ES:COMSEG,SS:COMSEG
  277.  
  278.           ORG    0                   
  279.   seg_org   EQU  $                     ; Start of loaded program
  280.  
  281.           ORG    2CH
  282. env_adr   LABEL  WORD                  ; offset of environment in PSP
  283.  
  284.           ORG    100H
  285.  
  286. main      PROC   FAR
  287. start:
  288.           JMP    booster               ; Set up portion
  289.  
  290.           .XLIST
  291.           SUBTTL Resident Part of Program
  292.           .LIST
  293.           PAGE
  294. ;-----------------------------------------------------------------------------
  295. ; DATA AREAS
  296. ;-----------------------------------------------------------------------------
  297.  
  298. old_interrupt1  DD ?                   ; Original branch for calling interrupt
  299.  
  300. on_line_number DB DEFAULT_ON_LINE_NUMBER
  301. scanline  DB     OFF_LINE_NUMBER
  302. palette_value DW 16 DUP (?)            ; High word is value to set
  303.                                        ; Low word is palette register to set
  304. palette_value_count DW 0
  305.  
  306. all_palettes DB  17 DUP (?)            ;      16 palette register values and
  307.                                        ; overscan register value for border
  308. reset_palette DB 0                     ; 0 means don't reset palette
  309. palette_data_length EQU $ - OFFSET palette_value
  310.  
  311. signature DW     (('U' SHL 8) OR 'N') OR 8080H
  312. test_string DW   0
  313.           DB     'UNDERLI2'
  314. test_string_length EQU $ - OFFSET test_string
  315.  
  316. ;-----------------------------------------------------------------------------
  317.  
  318. entry:
  319.           AND    AH,AH                 ;    Must be interrupt 10H, is it
  320.                                        ; set mode?
  321.           JZ     set_mode              ; Yes
  322.           JMP    CS:old_interrupt1     ; Go to BIOS and do not return
  323.  
  324. set_mode:
  325.           PUSHF                        ; Simulate interrupt
  326.           CALL   CS:old_interrupt1     ; Perform the called interrupt
  327.           PUSH   DS                    ; Minimal save registers
  328.           PUSH   AX
  329.           MOV    AX,CS                 ; Establish DS
  330.           MOV    DS,AX
  331.  
  332.           set_underlining
  333.           change_palette
  334.  
  335. interrupt_exit:
  336.           POP    AX                    ; Restore minimally saved registers
  337.           POP    DS
  338. empty_iret:
  339.           IRET                         ; Invoked by an interrupt
  340. main      ENDP
  341.  
  342. lst_byt:                               ; last byte to save
  343.  
  344. resident_length EQU (OFFSET lst_byt - seg_org + 15) shr 4
  345.                                        ; Number of paragraphs to save
  346.  
  347.  
  348.           .XLIST
  349.           SUBTTL Transient Part of Program (Booster)
  350.           .LIST
  351.           PAGE
  352. ;-----------------------------------------------------------------------------
  353. ; SUBROUTINES
  354. ;-----------------------------------------------------------------------------
  355.  
  356. ;     I modified these two procedures from "PC Magazine", Vol 6, No. 22
  357. ; (December 22, 1987), pp 366-7, for a COM program.  Ray Duncan wrote
  358. ; them.  Note that they do not support quoted arguments
  359.  
  360. ; ARGC.ASM:      return count of command line arguments.
  361. ;                Treats blanks and tabs as whitespace, carriage
  362. ;                returns as terminator
  363. ;
  364. ; (C) 1987 Ziff Communications co.
  365. ;
  366. ; Returns:       AX     = argument count (always >= 1)
  367. ;                Other registers preserved
  368. ;
  369.  
  370. argc      PROC   NEAR
  371.  
  372.           PUSH BX                      ; Save original BX
  373.           PUSH   CX
  374.           MOV    AX,1                  ; Force original count >= 1
  375.           MOV    BX,cmdtail            ; Beginning of command line
  376.  
  377. argc1:    MOV    CX,-1                 ; Set flag = outside argument
  378.  
  379. argc2:    INC    BX                    ; Point to next character
  380.           CMP    BYTE PTR [BX],cr
  381.           JE     argc3                 ; Exit if carriage return
  382.           CMP    BYTE PTR [BX],blank
  383.           JE     argc1                 ; Outside argument if ASCII blank
  384.           CMP    BYTE PTR [BX],tab
  385.           JE     argc1                 ; Outside argument if ASCII tab
  386.  
  387.           JCXZ   argc2                 ; Jump if already inside argument
  388.  
  389.           INC    AX                    ; Else found argument, count it
  390.           NOT    CX                    ; Set flag inside argument
  391.           JMP    argc2
  392.  
  393. argc3:    POP    CX                    ; Restore original BX and CX
  394.           POP    BX
  395.           ret
  396.  
  397. argc      ENDP
  398.  
  399. ;
  400. ; ARGV.ASM       Return address and length of specified
  401. ;                command line argument or fully qualified program
  402. ;                name.  Treats blanks and tabs as whitespace, cariage
  403. ;                returns as terminator.
  404. ;
  405. ; (C) 1987 Ziff Communications Co.
  406. ;
  407. ; Call with:     AX    = argument number (0 based)
  408. ;
  409. ; Returns:       BX    = argument offset
  410. ;                AX     = argument length
  411. ;                         (0=argument not found)
  412. ;                ES      = segment of environment block
  413. ;                          if argv[0] (see below)
  414. ;                         Other registers preserved.
  415. ;
  416. ; If called with AX=0 a(argv[0]) and running under
  417. ; MS-DOS version 3.0 or later, returns ES:BX pointing
  418. ; to program name in environment block and AX= length,
  419. ; otherwise returns ES:BX UNCHANGED AND AX=0.
  420. ;
  421.  
  422. argv      PROC   NEAR                  ; Get address and length of
  423.                                        ; command tail argument
  424.  
  425.           PUSH   CX                    ; Save original CX and DI
  426.           PUSH   DI
  427.  
  428.           OR     AX,AX                 ; Is it argument 0?
  429.           JZ     argv8                 ; Yes, jump to get program name
  430.  
  431.           MOV    BX,cmdtail            ; Beginning of command line
  432.           XOR    AH,AH                 ; Initialize argument counter
  433.  
  434. argv1:    MOV    CX,-1                 ; Set flag = outside argument
  435.  
  436. argv2:    INC    BX                    ; Point to next character
  437.           CMP    BYTE PTR [BX],cr
  438.           JE     argv7                 ; Exit if carriage return
  439.           CMP    BYTE PTR [BX],blank
  440.           JE     argv1                 ; Ouside argument if ASCII blank
  441.           CMP    BYTE PTR [BX],tab
  442.           JE     argv1                 ; Ouside argument if ASCII tab
  443.  
  444.                                        ; if not blank or tab...
  445.           JCXZ   argv2                 ; Jump if already inside argument
  446.           INC    AH                    ; Else count arguments found
  447.           CMP    AH,AL                 ; Is this the one we're looking for?
  448.           JE     argv4                 ; Yes, go find its length
  449.           NOT    CX                    ; No, set flag = iside argument
  450.           JMP    argv2                 ; and look at next character
  451.  
  452. argv4:                                 ; Found desired argument, now
  453.                                        ; determine its length...
  454.           MOV    AX,BX                 ; Save param, starting address
  455.  
  456. argv5:    INC    BX                    ; Point to next character
  457.           CMP    BYTE PTR [BX],cr
  458.           JE     argv6                 ; Found if carriage return
  459.           CMP    BYTE PTR [BX],blank
  460.           JE     argv6                 ; Found if ASCII blank
  461.           CMP    BYTE PTR [BX],tab
  462.           JNE    argv5                 ; Found if ASCII tab
  463.  
  464. argv6:    XCHG   BX,AX                 ; Set BX = argument offset
  465.           SUB    AX,BX                 ; And AX = argument length
  466.           JMP    SHORT argvx           ; Return to caller
  467.  
  468. argv7:    XOR    AX,AX                 ; Set AX = 0, argument not found
  469.           JMP    SHORT argvx           ; Return to caller
  470.  
  471. argv8:                                 ; Special handling for argv=0
  472.           MOV    AX,3000H              ; Check if DOS 3.0 or later
  473.           doscall                      ; (Force AL=0 in case DOS=1)
  474.           CMP    AL,3
  475.           JB     argv7                 ; DOS 1 or 2, return null param
  476.           MOV    ES,[env_adr]          ; Get environment segment from PSP
  477.           XOR    DI,DI                 ; Find program name by
  478.           XOR    AL,AL                 ; first skipping over all the
  479.           MOV    CX,-1                 ; environment variables
  480.           CLD
  481. argv9:    REPNE SCASB                  ; Scan for double null (can't use
  482.           SCASB                        ; SCASW since might be odd addr.)
  483.           JNE    argv9                 ; Loop if it was a single null
  484.           ADD    DI,2                  ; Skip count word in environment
  485.           MOV    BX,DI                 ; Save program name address
  486.           MOV    CX,-1                 ; Now find its length
  487.           REPNE SCASB                  ; Scan for another null byte
  488.           NOT    CX                    ; Convert CX to length
  489.           DEC    CX
  490.           MOV    AX,CX                 ; Return length in AX
  491.  
  492. argvx:                                 ; Common exit point
  493.           POP    DI                    ; Restore original CX and DI
  494.           POP    CX
  495.           RET
  496.  
  497. argv      ENDP
  498.           
  499. ; UPPER_CASE_COMMAND_LINE:
  500. ;                Set the original command line to upper case.        
  501. ;
  502. ; (C) 1987 Lew Paper
  503. ;
  504. ; Returns:       Nothing
  505. ;                All registers preserved
  506. ;
  507.  
  508. upper_case_command_line PROC   NEAR
  509.  
  510.           PUSH   AX                    ; Save original AX and BX
  511.           PUSH   BX
  512.           MOV    BX,cmdtail            ; Beginning of command line
  513.  
  514. upper_case_command_line_1:
  515.           INC    BX                    ; Point to next character
  516.           CMP    BYTE PTR [BX],'a'
  517.           JB     upper_case_command_line_2
  518.                                        ; Check for carriage return
  519.           CMP    BYTE PTR [BX],'z'
  520.           JA     upper_case_command_line_1
  521.           AND    BYTE PTR [BX],05FH    ; Convert to upper case
  522.           JMP    upper_case_command_line_1
  523.  
  524. upper_case_command_line_2:
  525.           CMP    BYTE PTR [BX],cr      ; Check for end of line
  526.           JNE    upper_case_command_line_1
  527.  
  528.           POP    BX                    ; Restore original AX and BX
  529.           POP    AX
  530.           ret
  531.  
  532. upper_case_command_line ENDP
  533.  
  534.           IF     prototype NE 0
  535.  
  536. ; BYTE_TO_DEC_99 - binary to ASCII decimal, 2 places
  537.  
  538. ; Call with AL = value to convert
  539. ;           DI = address for string
  540.  
  541. ; Stores leading zeros
  542.  
  543. ; Dan Daetwyler's binary to ASCII decimal conversion routines.
  544. ; Modified by Lew Paper to take advantage of word storing
  545.  
  546. byte_to_dec_99 PROC NEAR
  547.         CMP     AL,9                        ;One digit?
  548.         JG      byte_to_dec_2_digit         ;No
  549.         MOV     AH,' '                      ;Leading space
  550.         OR      AL,'0'                      ;Make one digit ASCII
  551.         JMP     SHORT byte_to_dec_save
  552.  
  553. byte_to_dec_2_digit:
  554.         AAM                     ;Number / 10 => AH
  555.                                             ;Number mod 10 => AL
  556.         OR      AX,'00'         ;make them ASCII
  557. byte_to_dec_save:
  558.         XCHG    AH,AL                       ;Number mod 10 => AH
  559.                                             ;Number / 10 => AL
  560.         STOSW                               ;Stores as AL, AH.  LP
  561.         RET                     ;back to caller
  562. byte_to_dec_99 ENDP
  563.  
  564.           ENDIF                        ; IF prototype NE 0
  565.  
  566. ;-----------------------------------------------------------------------------
  567. ; DATA FOR BOOSTER
  568. ;-----------------------------------------------------------------------------
  569.  
  570. ; Opening message for StdOut
  571. opening_msg DB cr, lf, 'UNDERLI2, modified by Lew Paper', cr, lf
  572.           DB 'from UNDERLIN: By Gerald S. Kaplan', cr, lf, lf, '$'
  573.  
  574. ; Result messages for StdOut
  575.  
  576. installed_msg DB 'Installed UNDERLI2 in memory', cr, lf, lf, '$'
  577.  
  578. modified_msg DB 'Modified existing UNDERLI2 in memory', cr, lf, lf, '$'
  579.  
  580. unloaded_msg DB  'Removed UNDERLI2 from memory', cr, lf, lf, '$'
  581.  
  582. ; Error messages for StdErr
  583.  
  584. no_EGA_msg DB    "You don't need UNDERLI2 because you don't "
  585.           DB     'have an installed EGA.'
  586.           DB     cr, lf, lf
  587. no_EGA_msg_length EQU $ - OFFSET no_EGA_msg
  588.  
  589. usage     DB     'Usage', cr, lf
  590.           DB     '       underli2 [on|off|switch|keep|unload|#] '
  591.           DB     '[/s ] [/p<k1>=<n1>] [/p<k2>= ...', cr, lf
  592.           DB     '       where', cr, lf
  593.           DB     '                "on" turns underlining on.', cr, lf
  594.           DB     '                "off" turns underline off.', cr, lf
  595.           DB     '                "switch" reverses the underlining setting.'
  596.           DB     cr, lf
  597.           DB     '                "keep" keeps the underlining setting.'
  598.           DB     cr, lf
  599.           DB     '                "unload" removes underli2 from memory.'
  600.           DB     cr, lf
  601.           DB     '                # turns on underlining at scan line #.'
  602.           DB     cr,lf
  603.           DB     "                     If you don't specify one of these "
  604.           DB     'values,', cr, lf
  605.           DB     '                underli2 reverses the underlining setting.'
  606.           DB     cr, lf
  607.           DB     '                     "/s" resets all unspecified palettes '
  608.           DB     'to their', cr, lf
  609.           DB     '                standard values.', cr, lf
  610.           DB     '                     "/p<ki>=<ni>" sets palette ki '
  611.           DB     '(maximum 15)', cr, lf
  612.           DB     '                to value ni (maximum 63 = 03FH).  Enter '
  613.           DB     'ki and ni', cr, lf
  614.           DB     '                as decimal numbers.'
  615.           DB     cr, lf, lf
  616. usage_length EQU $ - OFFSET usage
  617.  
  618. wrong_DOS_msg DB 'UNDERLI2 requires DOS 2 or DOS 3'
  619.           DB     cr, lf, lf
  620. wrong_DOS_msg_length EQU $ - OFFSET wrong_DOS_msg
  621.  
  622. memory_alloc_error_msg DB 'IS_RESIDENT in UNDERLI2 '
  623.           DB     'found a memory allocation error'
  624.           DB     cr, lf, lf
  625. memory_alloc_error_msg_length EQU $ - OFFSET memory_alloc_error_msg
  626.  
  627. not_resident_error_msg DB "UNDERLI2 isn't resident, "
  628.           DB     'so there is nothing to UNLOAD'
  629.           DB     cr, lf, lf
  630. not_resident_error_msg_length EQU $ - OFFSET not_resident_error_msg
  631.           
  632. not_end_of_chain_msg DB "You can't UNLOAD UNDERLI2 because INT 10H jumps "
  633.           DB     'to another program first'
  634.           DB     cr, lf, lf
  635. not_end_of_chain_msg_length EQU $ - OFFSET not_end_of_chain_msg
  636.  
  637. argument_count DW 0
  638. palette_value_flag DB 16 DUP (0)
  639. standard_palette_values DB 00H         ; High word is value to set
  640.                                        ; Low word is palette register to set
  641.           DB     01H, 02H, 03H
  642.           DB     04H, 05H, 06H, 07H
  643.           DB     38H, 39H, 3AH, 3BH
  644.           DB     3CH, 3DH, 3EH, 3FH
  645.           DB     0                     ; Overscan register
  646. exitval   DB     0                     ; Exit value
  647. is_resident_value DB 0FFH              ; 0 if not resident, 1 if resident
  648. old_UNDERLI2_segment DW (?)
  649.  
  650. ; Parameter values for underline_action
  651.           underline_action_string ON
  652.           underline_action_string OFF
  653.           underline_action_string SWITCH
  654.           underline_action_string KEEP
  655.           underline_action_string UNLOAD
  656.  
  657. underline_action DB NONE
  658.  
  659. fail49    DB     cr, lf, "DOS function 49H error", cr, lf
  660.           DB     "Failed to free environment block", cr, lf, cr, lf
  661. fail49_length EQU $ - OFFSET fail49
  662.  
  663.           IF     prototype EQ 1
  664. param_list DB    'underline_action = '
  665. ula       DW     (?)
  666.           DB     cr, lf, 'on_line_number   = '
  667. oln       DW     (?)
  668.           DB     cr, lf, 'scanline         = '
  669. sl        DW     (?)
  670.           DB     cr, lf, 'CHANGED PALETTE VALUES'
  671.           DB     cr, lf, 'Palette'
  672.           DB     cr, lf, ' Number  Value$'
  673. pl        DB     cr, lf, '   '
  674. pln       DW     (?)
  675.           DB                  '      '
  676. plv       DW     (?)
  677.           DB     '$'
  678. crlf2     db     cr, lf, cr, lf, '$'
  679.           ENDIF                        ; IF prototype EQ 1
  680. ;-----------------------------------------------------------------------------
  681.  
  682. booster   PROC   NEAR                  ; Set up resident program
  683.  
  684.           dis_str opening_msg
  685.           MOV    BX,DS                 ; Check for EGA with the method from
  686.                                        ; Robert Jourdain, "PROGRAMMER'S
  687.                                        ; PROBLEM SOLVER for the IBM PC, XT
  688.                                        ; and AT" (New York, Brady 
  689.                                        ; Communications Company Inc., 1986),
  690.                                        ; p. 9.
  691.                                        ; Save DS
  692.           MOV    AX,40H                ; Segment for BIOS data area
  693.           MOV    DS,AX
  694.           MOV    AL,[87H]              ; 487H is 0 if no EGA is present
  695.           MOV    DS,BX                 ; Restore DS
  696.           AND    AL,AL                 ; Is EGA present?
  697.           JNZ    EGA_present
  698.           write_str no_EGA_msg, StdErr
  699.           MOV    AL,1
  700.           JMP    exit_loc
  701.  
  702. EGA_present:
  703.           CALL   argc
  704.           CMP    AX,1
  705.           JG     read_command_line
  706.           MOV    underline_action,SWITCH
  707.           JMP    check_residence
  708.  
  709. read_command_line:
  710.           CALL   upper_case_command_line
  711.           MOV    argument_count,AX
  712.           MOV    AX,1
  713.  
  714. get_parameter:
  715.           PUSH   AX
  716.           CALL   argv
  717.           MOV    CX,AX                 ; Length of argument
  718.           CMP    BYTE PTR [BX],'/'     ; Check for palette value
  719.           JZ     check_argument_type
  720.           CMP    BYTE PTR [BX],'-'
  721.           JZ     check_argument_type
  722.           CMP    underline_action,NONE ; Are two actions specified?
  723.           JZ     set_underline_action_1
  724.           JMP    usage_exit
  725.  
  726. set_underline_action_1:                ; Avoid relative jump out of range
  727.           JMP    set_underline_action
  728.  
  729. check_argument_type:
  730.           INC    BX
  731.           DEC    CX
  732.           JCXZ   usage_error_1
  733.           CMP    BYTE PTR [BX],'S'
  734.           JE     check_reset_palette_request
  735.           CMP    BYTE PTR [BX],'P'
  736.           JNE    usage_error_1
  737.           INC    BX                    ; Must have palette register number
  738.           DEC    CX
  739.           JCXZ   usage_error_1
  740.           JMP    SHORT get_palette_number
  741.  
  742. check_reset_palette_request:
  743.           CMP    reset_palette,0       ; Has "/s" already been specified?
  744.           JNZ    usage_error_1         ; Yes
  745.           DEC    CX                    ; Must be end of argument
  746.           JCXZ   set_reset_palette
  747.  
  748. usage_error_1:                         ; Avoid relative jump out of range
  749.           JMP    usage_exit
  750.  
  751. set_reset_palette:
  752.           MOV    reset_palette,1
  753.           JMP    get_parameter_loop
  754.  
  755. get_palette_number:
  756.           convert_bx_to_integer        ; Palette number
  757.           JC     usage_error_1
  758.           CMP    AX,15
  759.           JA     usage_error_1
  760.           MOV    SI,AX
  761.           CMP    palette_value_flag[SI],0   
  762.                                        ; Has it already been set?
  763.           JNE    usage_error_1
  764.                                        ; Yes
  765.           MOV    palette_value_flag[SI],1
  766.                                        ; Set to avoid duplicates
  767.           MOV    SI,palette_value_count
  768.                                        ;    Number of next open word in 
  769.                                        ; palette_value
  770.           SHL    SI,1                  ; Convert to word index
  771.           MOV    palette_value[SI],AX  ; Palette register to be set
  772.           JCXZ   palette_value_in_next_arg
  773.           INC    BX                    ; Bypass filler character
  774.           DEC    CX
  775.           JCXZ   set_palette_value_error
  776. get_palette_value:
  777.           convert_bx_to_integer
  778.           JC     set_palette_value_error
  779.           JCXZ   check_palette_value   ; Must be end of argument
  780.           JMP    SHORT set_palette_value_error
  781.  
  782. palette_value_in_next_arg:
  783.           POP    AX
  784.           INC    AX
  785.           CMP    AX,argument_count
  786.           JGE    set_palette_value_error
  787.                                        ; No next argument
  788.           PUSH   AX
  789.           CALL   argv
  790.           MOV    CX,AX
  791.           JMP    get_palette_value
  792.  
  793. check_palette_value:
  794.           CMP    AX,03FH               ; Must be less than 64
  795.           JA     set_palette_value_error
  796.           XCHG   AL,AH                 ; palette_value => AH
  797.                                        ; 0 => AL
  798.           OR     palette_value[SI],AX
  799.                                        ; Save palette value
  800.           INC    palette_value_count
  801.           JMP    get_parameter_loop
  802.  
  803. set_palette_value_error:               ; Avoid relative jump out of range
  804.           JMP    usage_exit
  805.  
  806. set_underline_action:
  807.           MOV    SI,BX                 ; Offset of argument
  808.           convert_bx_to_integer
  809.           JC     underline_actions
  810.           JCXZ   reset_scanline
  811.           JMP    usage_exit
  812.  
  813. reset_scanline:
  814.           MOV    on_line_number,AL
  815.           MOV    underline_action,NUMBER
  816.           JMP    SHORT get_parameter_loop
  817.  
  818. underline_actions:
  819.           check_underline_action ON
  820.           check_underline_action OFF
  821.           check_underline_action SWITCH
  822.           check_underline_action KEEP
  823.           check_underline_action UNLOAD
  824.           JMP    usage_exit
  825.           
  826. get_parameter_loop:
  827.           POP    AX
  828.           INC    AX
  829.           CMP    AX,argument_count
  830.           JGE    check_reset_palette
  831.           JMP    get_parameter
  832.  
  833. check_reset_palette:
  834.           MOV    AL,reset_palette
  835.           AND    AL,AL                 ; Reset unspecified palette registers?
  836.           JZ     check_residence       ; No
  837.           MOV    CX,palette_value_count
  838.                                        ;    Any palette value registers
  839.                                        ; specified?
  840.           JCXZ   set_all_palettes      ; NO
  841.           MOV    SI,OFFSET palette_value
  842.           XOR    BH,BH                 ;      BX will index the changes
  843.                                        ; in standard_palette_values
  844. change_std_palette_values:
  845.           LODSW                        ; Changed palette value => AX
  846.                                        ;      Point SI at next changed 
  847.                                        ; palette value
  848.           MOV    BL,AL
  849.           MOV    standard_palette_values[BX],AH
  850.           LOOP   change_std_palette_values
  851.  
  852. set_all_palettes:
  853.           MOV    SI,OFFSET standard_palette_values
  854.           MOV    DI,OFFSET all_palettes
  855.           MOV    CX,17
  856.           REP    MOVSB
  857.  
  858. check_residence:
  859.           IF     prototype EQ 1
  860.           MOV    DI,OFFSET ula
  861.           MOV    AL,underline_action
  862.           CALL   byte_to_dec_99
  863.           MOV    DI,OFFSET oln
  864.           MOV    AL,on_line_number
  865.           CALL   byte_to_dec_99
  866.           MOV    DI,OFFSET sl
  867.           MOV    AL,scanline
  868.           CALL   byte_to_dec_99
  869.           dis_str param_list
  870.           CMP    reset_palette,0
  871.           JZ     not_reset_1
  872.           MOV    CX,17
  873.           MOV    SI,OFFSET all_palettes
  874. pv_reset_loop:
  875.           MOV    AX,SI
  876.           SUB    AX,OFFSET all_palettes
  877.           MOV    DI,OFFSET pln
  878.           CALL   byte_to_dec_99
  879.           XOR    AH,AH
  880.           LODSB
  881.           MOV    DI,OFFSET plv
  882.           CALL   byte_to_dec_99
  883.           dis_str pl
  884.           LOOP   pv_reset_loop
  885.           JMP    SHORT after_pv
  886.  
  887. not_reset_1:
  888.           MOV    CX,palette_value_count
  889.           JCXZ   after_pv
  890.           MOV    SI,OFFSET palette_value
  891. pv_loop:
  892.           MOV    AX,[SI]
  893.           XOR    AH,AH
  894.           MOV    DI,OFFSET pln
  895.           CALL   byte_to_dec_99
  896.           LODSW
  897.           XCHG   AL,AH
  898.           XOR    AH,AH
  899.           MOV    DI,OFFSET plv
  900.           CALL   byte_to_dec_99
  901.           dis_str pl
  902.           LOOP   pv_loop
  903. after_pv:
  904.           dis_str crlf2
  905.           ELSE                         ; prototype EQ 0
  906.           MOV    SI,OFFSET signature   ; Set up for is_resident
  907.           MOV    DI,OFFSET test_string
  908.           MOV    AX,test_string_length
  909.           CALL   is_resident
  910.           CMP    AX,-1                 ; Was there an error?
  911.           JG     check_UNLOAD          ; No
  912.           MOV    BX,ES                 ; Restore DS
  913.           MOV    DS,BX
  914.           JZ     wrong_DOS
  915.           write_str memory_alloc_error_msg, StdErr
  916.           MOV    AL,4
  917.           JMP    exit_loc
  918.  
  919. wrong_DOS:
  920.           write_str wrong_DOS_msg, StdErr
  921.           MOV    AL,3
  922.           JMP    exit_loc
  923.  
  924. check_UNLOAD:
  925.           CMP    ES:underline_action,UNLOAD
  926.           JZ     anything_to_UNLOAD
  927.           JMP    not_UNLOAD
  928.  
  929. anything_to_UNLOAD:
  930.           OR     AX,AX                 ; Is a copy of UNDERLI2 resident?
  931.           JNZ    check_end_of_chain    ; Yes
  932.           write_str not_resident_error_msg, StdErr
  933.           MOV    AL,5
  934.           JMP    exit_loc
  935.  
  936. check_end_of_chain:
  937.           MOV    ES:old_UNDERLI2_segment,DS
  938.           MOV    AX,ES                 ; Set DS to this incarnation
  939.           MOV    DS,AX
  940.           MOV    AX,3500H OR resident_int
  941.                                        ; Get interrupt vector
  942.           doscall
  943.           MOV    AX,ES                 ; Segment of interrupt vector
  944.           CMP    AX,old_UNDERLI2_segment
  945.           JNZ    not_end_of_chain
  946.           CMP    BX,OFFSET entry       ; Offset of interrupt vector
  947.           JZ     end_of_chain
  948. not_end_of_chain:
  949.           write_str not_end_of_chain_msg, StdErr
  950.           MOV    AL,6
  951.           JMP    exit_loc
  952.  
  953. end_of_chain:
  954.           MOV    AH,49H                ; Prepare to release memory
  955.           MOV    ES,old_UNDERLI2_segment
  956.           MOV    CX,DS                 ; Save this segment
  957.           LDS    DX,ES:old_interrupt1  ;      To restore interrupt if
  958.                                        ; release memory works
  959.           doscall                      ; Now release memory
  960.           JNC    release_memory_worked
  961.           MOV    DS,CX                 ; Restore segment
  962.           write_str fail49, StdErr
  963.           MOV    AL,7
  964.           JMP    exit_loc
  965.  
  966. release_memory_worked:
  967.           MOV    AX,2500H OR resident_int
  968.                                        ; Restore old interrupt
  969.           CLI
  970.           doscall
  971.           STI
  972.           MOV    DS,CX                 ; Restore segment
  973.           dis_str unloaded_msg
  974.           JMP    set_error_level
  975.           
  976. not_UNLOAD:
  977.           MOV    ES:is_resident_value,AL
  978.                                        ; ES is segment for this incarnation
  979.           CMP    ES:underline_action,ON
  980.                                        ; Now set scanline
  981.           JNZ    not_ON
  982. set_scanline_ON:
  983.           MOV    AL,on_line_number     ; Resident incarnation
  984.           MOV    scanline,AL
  985.           JMP    SHORT scanline_set
  986.  
  987. not_ON:
  988.           CMP    ES:underline_action,OFF
  989.           JNZ    not_OFF
  990. set_scanline_OFF:
  991.           MOV    scanline,OFF_LINE_NUMBER
  992.           JMP    SHORT scanline_set
  993.  
  994. not_OFF:
  995.           CMP    ES:underline_action,SWITCH
  996.           JNZ    not_SWITCH
  997.           CMP    scanline,OFF_LINE_NUMBER
  998.           JZ     set_scanline_ON
  999.           JMP    SHORT set_scanline_OFF
  1000.  
  1001. not_SWITCH:
  1002.           CMP    ES:underline_action,NUMBER
  1003.           JNZ    scanline_set          ; Must be keep
  1004.           MOV    AL,ES:on_line_number  ;    Move new line number to
  1005.                                        ; resident incarnation.  It is
  1006.                                        ; cheaper to move to itself than
  1007.                                        ; to test and branch
  1008.           MOV    on_line_number,AL
  1009.           JMP    SHORT set_scanline_ON
  1010.  
  1011. scanline_set:
  1012.           TEST   ES:is_resident_value,0FFH
  1013.                                        ; Is UNDERLIN2 already resident?
  1014.           JZ     install               ; No
  1015.           MOV    BX,DS                 ;      Move all palette data to
  1016.                                        ; installed incarnation
  1017.                                        ; Installed incarnation segment => ES
  1018.                                        ; This incarnation segment => DS
  1019.           MOV    DX,ES
  1020.           MOV    DS,DX
  1021.           MOV    ES,BX
  1022.           MOV    SI,OFFSET palette_value
  1023.           MOV    DI,OFFSET palette_value
  1024.           MOV    CX,palette_data_length
  1025.           REP    MOVSB
  1026.           MOV    DS,BX                 ; Restore segments
  1027.                                        ; Installed incarnation segment => DS
  1028.                                        ; This incarnation segment => ES
  1029.           MOV    ES,DX
  1030.           JMP    SHORT run_from_command_line
  1031.  
  1032. install:
  1033. ; Free memory allocated for environment.  If DOS can not do it, return
  1034. ; without installing program and with error code 6.  
  1035.           MOV    ES,env_adr            ; Get address of environment
  1036.           MOV    AH,49H                ; Free allocated memory
  1037.           doscall                      ; Call MS-DOS
  1038.           JNC    setvect               ; Branch if no error
  1039.           write_str fail49, StdErr     ; Inform that there was an error
  1040.           MOV    AL,7
  1041.           JMP    exit_loc
  1042.  
  1043. setvect:                               ; Environment removed
  1044.           
  1045. ;         Save old interrupt
  1046.           MOV    AH,35H                ; Get interrupt vector
  1047.           MOV    AL,resident_int       ; Interrupt number
  1048.           doscall
  1049.           MOV    WORD PTR old_interrupt1,BX
  1050.                                        ; Offset of interrupt vector
  1051.           MOV    WORD PTR old_interrupt1[2],ES
  1052.                                        ; Segment of interrupt vector
  1053.           MOV    AX,CS                 ; Reestablish ES
  1054.           MOV    ES,AX
  1055.           OR     BX,WORD PTR old_interrupt1[2]
  1056.                                        ; Were both segment and offset zero?
  1057.           JNZ    set_new_interrupt     ; No
  1058.           MOV    WORD PTR old_interrupt1,OFFSET empty_iret
  1059.                                        ; Point to just an IRET
  1060.           MOV    WORD PTR old_interrupt1[2],CS
  1061.  
  1062. set_new_interrupt:
  1063.           MOV    DX,OFFSET entry       ; Entry point for interrupt
  1064.           MOV    AL,resident_int       ; Interrupt number to reset
  1065.           MOV    AH,25H                ; Set interrupt number
  1066.           CLI                          ; Disable interrupts
  1067.           doscall
  1068.           STI                          ; Enable interrupts
  1069.  
  1070. run_from_command_line:
  1071.           change_palette
  1072.           CMP    underline_action,KEEP
  1073.           JZ     booster_exit
  1074.           set_underlining
  1075.  
  1076. booster_exit:
  1077.           TEST   ES:is_resident_value,0FFH
  1078.                                        ; Did you install this time?
  1079.           JNZ    display_modified_msg  ; No
  1080.  
  1081. ; Terminate and stay resident
  1082.           dis_str installed_msg
  1083.           MOV    DX,resident_length    ; Number of paragraphs to save
  1084.           MOV    AX,3100H              ; Terminate process and remain
  1085.                                        ; resident.  Return code = 0.
  1086.           doscall                      ; Call MS-DOS
  1087.  
  1088. display_modified_msg:
  1089.           MOV    AX,ES                 ; Restore DS to this incarnation
  1090.           MOV    DS,AX
  1091.           dis_str modified_msg
  1092.  
  1093.           ENDIF                        ; IF prototype EQ 1
  1094.  
  1095. set_error_level:
  1096.           MOV    AL,exitval            ; Set error level
  1097.           JMP    SHORT exit_loc
  1098.  
  1099. usage_exit:
  1100.           write_str usage,StdErr
  1101.           MOV    AL,2
  1102.  
  1103. exit_loc: MOV    AH,4CH                ; Terminate process function number
  1104.           doscall                      ; Return to DOS
  1105.  
  1106. booster   ENDP
  1107.  
  1108. ;-----------------------------------------------------------------------------
  1109. ; OVERALL END
  1110. ;-----------------------------------------------------------------------------
  1111. COMSEG    ENDS
  1112.           END    start
  1113.  
  1114.  
  1115.